package com.erlin.platform.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.erlin.common.api.UserInfoVo;
import com.erlin.common.api.param.WxRegisterParam;
import com.erlin.common.client.WxClient;
import com.erlin.common.client.WxClientRes;
import com.erlin.common.client.WxUserConstant;
import com.erlin.common.config.properties.WxProperties;
import com.erlin.common.entity.ElPermission;
import com.erlin.common.exception.ElException;
import com.erlin.common.security.entity.SysUserDetail;
import com.erlin.common.utils.JwtTokenUtils;
import com.erlin.common.utils.MessageUtils;
import com.erlin.platform.constant.user.UserRoleEnum;
import com.erlin.platform.constant.user.UserStatus;
import com.erlin.platform.controller.param.AddNewUserParam;
import com.erlin.platform.entity.ElUser;
import com.erlin.platform.entity.UserRoleMid;
import com.erlin.platform.mapper.ElUserMapper;
import com.erlin.platform.service.IElUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.erlin.platform.service.IUserRoleMidService;
import org.aspectj.bridge.MessageUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * <p>
 * 后台用户表 服务实现类
 * </p>
 *
 * @author alin
 * @since 2021-05-16
 */
@Service
public class ElUserServiceImpl extends ServiceImpl<ElUserMapper, ElUser> implements IElUserService {
    private final String usernamePattern = "^[a-zA-Z0-9_-]{4,16}$";
    private final String phonePattern = "^1[3456789]d{9}$";
    @Autowired
    private IUserRoleMidService userRoleMidService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtTokenUtils jwtTokenUtils;
    @Autowired
    private ElUserMapper elUserMapper;
    @Autowired
    private WxClient wxClient;
    @Autowired
    private WxProperties wxProperties;

    @Override
    @Transactional
    public Boolean addUser(AddNewUserParam param) {
        checkAddData(param);
        var elUser = new ElUser();
        BeanUtils.copyProperties(param, elUser);
        elUser.setPassword(passwordEncoder.encode(param.getPassword()));
        elUser.setStatus(UserStatus.NORMAL);
        elUser.setUserClas(UserRoleEnum.ADMIN.getUserClas());
        save(elUser);
        var userRoleMid = new UserRoleMid();
        userRoleMid.setUserId(elUser.getId());
        userRoleMid.setRoleId(Objects.isNull(param.getRoleId()) ? UserRoleEnum.ADMIN.getCode() : UserRoleEnum.CUSTOMER.getCode());
        userRoleMidService.save(userRoleMid);
        return true;
    }

    @Override
    public String login(String username, String password) {
        ElUser elUser = Optional.ofNullable(getOne(Wrappers.<ElUser>lambdaQuery().eq(ElUser::getUsername, username))).orElseThrow(() -> new ElException("用户未注册!"));
        if (!passwordEncoder.matches(password, elUser.getPassword())) {
            throw new ElException("密码错误！");
        }
        //TODO 查询相关权限  jdk11 HTTPCLIENT
        return authAndReturnToken(elUser);
    }

    private String authAndReturnToken(ElUser elUser) {
        List<ElPermission> list = new ArrayList<>();
        SysUserDetail sysUserDetail = new SysUserDetail(elUser, list);
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(elUser, null, sysUserDetail.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = jwtTokenUtils.generateToken(sysUserDetail);
        return token;
    }

    @Override
    public ElUser getUserByName(String username) {
        return getOne(Wrappers.<ElUser>lambdaQuery().eq(ElUser::getUsername, username));
    }

    @Override
    public List<ElPermission> getPermissionList(Long id) {
        return Optional.ofNullable(elUserMapper.getPermissionList(id)).orElse(Collections.emptyList());
    }

    @Override
    public UserInfoVo wxLogin(String code) {
        WxClientRes wxUser = null;
        try {
            wxUser = wxClient.getWxUser(wxProperties.getAppid(), wxProperties.getSecret(), code);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("调用微信接口失败！失败原因：" + e.getMessage());
            throw new ElException(MessageUtils.getMessage("wx.request.faild"));
        }
        if (Objects.isNull(wxUser) || !Objects.equals(WxUserConstant.SUCCESS, wxUser.getErrcode())) {
            throw new ElException(MessageUtils.getMessage("wx.request.faild"));
        }
        //查看此openId用户是否是系统内用户。
        // 如果是，则返回token以及用户信息
        // 如果不是，不返回token和用户信息
        // 让前端进行调用wx服务进行授权获取其相关信息进行注册并返回token
        ElUser one = getOne(Wrappers.<ElUser>lambdaQuery().eq(ElUser::getOpenId, wxUser.getOpenid()));
        UserInfoVo result = new UserInfoVo();
        if (Objects.isNull(one)) {
            result.setOpenId(wxUser.getOpenid());
            return result;
        }
        result.setToken(authAndReturnToken(one));
        BeanUtils.copyProperties(one, result);
        return result;
    }

    @Override
    @Transactional
    public UserInfoVo wxRegister(WxRegisterParam param) {
        checkRegisterParam(param);
        //注册进用户表并且增加对应默认的角色信息
        ElUser target = new ElUser();
        target.init();
        BeanUtils.copyProperties(param,target);
        target.setPassword(passwordEncoder.encode(target.getOpenId())); //使用openId进行加密后当作密码，TODO 修改
        save(target);
        //返回token
        userRoleMidService.insert(target.getId(),UserRoleEnum.CUSTOMER);//添加默认身份
        var result = new UserInfoVo();
        result.setToken(authAndReturnToken(target));
        BeanUtils.copyProperties(target,result);
        return result;
    }

    private void checkRegisterParam(WxRegisterParam param) {
        //TODO check
        if (Objects.equals(UserRoleEnum.CUSTOMER.toString(), param.getUserClas())) {
            log.warn("【注册警告】：有用户使用非初始化角色进行注册已拦截！openid：[" + param.getOpenId() + "],电话：[" + param.getPhoneNum() + "]");
            throw new ElException(MessageUtils.getMessage("user.role.init.error"));
        }
        checkPhoneNum(param.getPhoneNum());
    }

    private void checkPhoneNum(String phoneNum) {
        if (phoneNum.matches(phonePattern)) {
            throw new ElException(MessageUtils.getMessage("user.phone.error"));
        }
        if (Optional.ofNullable(getOne(Wrappers.<ElUser>lambdaQuery().eq(ElUser::getPhoneNum, phoneNum))).isPresent()) {
            throw new ElException(MessageUtils.getMessage("user.phone.exist"));
        }
    }

    private void checkAddData(AddNewUserParam param) {
        if (Optional.ofNullable(getOne(Wrappers.<ElUser>lambdaQuery().eq(ElUser::getUsername, param.getUsername()))).isPresent()) {
            throw new ElException(MessageUtils.getMessage("user.username.exist"));
        }
        if (!param.getUsername().matches(usernamePattern)) {
            throw new ElException(MessageUtils.getMessage("user.username.error"));
        }
        if (param.getPassword().length() < 4 || param.getPassword().length() > 20) {
            throw new ElException(MessageUtils.getMessage("user.password.error"));
        }
        checkPhoneNum(param.getPhoneNum());
    }


}
